/**
  ******************************************************************************
  * @file    usbh_diskio.c 
  * @author  MCD Application Team
  * @version V1.3.0
  * @date    08-May-2015
  * @brief   USB Key Disk I/O driver.
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; COPYRIGHT 2015 STMicroelectronics</center></h2>
  *
  * Licensed under MCD-ST Liberty SW License Agreement V2, (the "License");
  * You may not use this file except in compliance with the License.
  * You may obtain a copy of the License at:
  *
  *        http://www.st.com/software_license_agreement_liberty_v2
  *
  * Unless required by applicable law or agreed to in writing, software 
  * distributed under the License is distributed on an "AS IS" BASIS, 
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  *
  ******************************************************************************
  */ 

/* Includes ------------------------------------------------------------------*/
#include <string.h>
#include "ff_gen_drv.h"
#include "diskio.h"
#include "usb_conf.h"
#include "usbh_msc_core.h"

/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
static volatile DSTATUS stat = STA_NOINIT; /* disk status */

extern usb_core_handle_struct          usb_core_dev;
extern usbh_host_struct                usb_host;
extern usbh_state_handle_struct        usbh_state_core;
//extern USBH_HandleTypeDef  HOST_HANDLE;
#if _USE_BUFF_WO_ALIGNMENT == 0
/* Local buffer use to handle buffer not aligned 32bits*/
//static DWORD scratch[_MAX_SS / 4];
#endif

/* Private function prototypes -----------------------------------------------*/
DSTATUS USBH_initialize (BYTE);
DSTATUS USBH_status (BYTE);
DRESULT USBH_read (BYTE, BYTE*, DWORD, UINT);

#if _USE_WRITE == 1
  DRESULT USBH_write (BYTE, const BYTE*, DWORD, UINT);
#endif /* _USE_WRITE == 1 */

#if _USE_IOCTL == 1
  DRESULT USBH_ioctl (BYTE, BYTE, void*);
#endif /* _USE_IOCTL == 1 */
  
const Diskio_drvTypeDef  USBH_Driver =
{
  USBH_initialize,
  USBH_status,
  USBH_read, 
#if  _USE_WRITE == 1
  USBH_write,
#endif /* _USE_WRITE == 1 */  
#if  _USE_IOCTL == 1
  USBH_ioctl,
#endif /* _USE_IOCTL == 1 */
};

/* Private functions ---------------------------------------------------------*/

/**
  * @brief  Initializes a Drive
  * @param  lun : lun id
  * @retval DSTATUS: Operation status
  */
DSTATUS USBH_initialize(BYTE lun)
{
    if (hcd_is_device_connected(&usb_core_dev)) {
        stat &= ~STA_NOINIT;
    }

    return stat;
}

/**
  * @brief  Gets Disk Status
  * @param  lun : lun id
  * @retval DSTATUS: Operation status
  */
DSTATUS USBH_status(BYTE lun)
{
    /* supports only single drive */
    if (lun) return STA_NOINIT; 

    return stat;
}

/**
  * @brief  Reads Sector(s) 
  * @param  lun : lun id
  * @param  *buff: Data buffer to store read data
  * @param  sector: Sector address (LBA)
  * @param  count: Number of sectors to read (1..128)
  * @retval DRESULT: Operation result
  */
DRESULT USBH_read(BYTE lun, BYTE *buff, DWORD sector, UINT count)
{
    BYTE status = USBH_MSC_OK;

    if (lun || !count) return RES_PARERR;
    if (stat & STA_NOINIT) return RES_NOTRDY;

    if (hcd_is_device_connected(&usb_core_dev)) {
        do {
            status = usbh_msc_read10(&usb_core_dev, buff,sector, 512 * count);
            usbh_msc_handle_botxfer(&usb_core_dev, &usb_host,  &usbh_state_core);

            if (!hcd_is_device_connected(&usb_core_dev)) {
                return RES_ERROR;
            }
        }
        while(USBH_MSC_BUSY == status);
    }

    if (USBH_MSC_OK == status) return RES_OK;

    return RES_ERROR;
}

/**
  * @brief  Writes Sector(s)
  * @param  lun : lun id 
  * @param  *buff: Data to be written
  * @param  sector: Sector address (LBA)
  * @param  count: Number of sectors to write (1..128)
  * @retval DRESULT: Operation result
  */
#if _USE_WRITE == 1
DRESULT USBH_write(BYTE lun, const BYTE *buff, DWORD sector, UINT count)
{
    BYTE status = USBH_MSC_OK;
    if (lun || !count) return RES_PARERR;
    if (stat & STA_NOINIT) return RES_NOTRDY;
    if (stat & STA_PROTECT) return RES_WRPRT;

    if (hcd_is_device_connected(&usb_core_dev)) {
        do {
            status = usbh_msc_write10(&usb_core_dev,(BYTE*)buff,sector,512 * count);
            usbh_msc_handle_botxfer(&usb_core_dev, &usb_host,  &usbh_state_core);

            if (!hcd_is_device_connected(&usb_core_dev)) {
                return RES_ERROR;
            }
        } while(USBH_MSC_BUSY == status);
    }

    if (USBH_MSC_OK == status) return RES_OK;

    return RES_ERROR;
}
#endif /* _USE_WRITE == 1 */

/**
  * @brief  I/O control operation
  * @param  lun : lun id
  * @param  cmd: Control code
  * @param  *buff: Buffer to send/receive control data
  * @retval DRESULT: Operation result
  */
#if _USE_IOCTL == 1
DRESULT USBH_ioctl(BYTE lun, BYTE cmd, void *buff)
{
   DRESULT res = RES_OK;

    if (lun) return RES_PARERR;

    res = RES_ERROR;

    if (stat & STA_NOINIT) return RES_NOTRDY;

    switch (cmd) {
        case CTRL_SYNC:         /* make sure that no pending write process */
            res = RES_OK;
            break;

        case GET_SECTOR_COUNT:  /* get number of sectors on the disk (DWORD) */
            *(DWORD*)buff = (DWORD) usbh_msc_param.msc_capacity;
            res = RES_OK;
            break;

        case GET_SECTOR_SIZE:  /* get R/W sector size (WORD) */
            *(WORD*)buff = 512U;
            res = RES_OK;
            break;

        case GET_BLOCK_SIZE:   /* get erase block size in unit of sector (DWORD) */
            *(DWORD*)buff = 512U;
            break;

        default:
            res = RES_PARERR;
            break;
    }

    return res;
}
#endif /* _USE_IOCTL == 1 */

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

